﻿namespace Devonline.Entity;

/// <summary>
/// 数据加密业务抽象基类
/// 字符串类型的默认基类
/// </summary>
public abstract class EntitySetWithDataSecurity : EntitySetWithDataSecurity<string>, IDataSecurity, IEntitySet, IEntitySetWithCreate
{
    /// <summary>
    /// 构造方法给 Id 赋值
    /// </summary>
    public EntitySetWithDataSecurity() => Id = KeyGenerator.GetStringKey();
}

/// <summary>
/// 数据加密业务抽象基类
/// </summary>
public abstract class EntitySetWithDataSecurity<TKey> : EntitySetWithCreate<TKey>, IDataSecurity, IEntitySet<TKey>, IEntitySetWithCreate<TKey> where TKey : IConvertible
{
    /// <summary>
    /// 数据加密模式
    /// </summary>
    [Column("encryption_mode", TypeName = "varchar(16)"), DisplayName("数据加密模式"), Required, DefaultValue(DataEncryptionMode.KeyId), Excel]
    public DataEncryptionMode EncryptionMode { get; set; } = DataEncryptionMode.KeyId;
    /// <summary>
    /// 数据版本号, 用于给数据增加一个变量, 在计算 Hash 值时增加不确定因素
    /// </summary>
    [Column("version"), DisplayName("数据版本号"), Required, MaxLength(36), Excel]
    public virtual string Version { get; set; } = KeyGenerator.GetStringKey();
    /// <summary>
    /// 数据 Hash 值, 用于确认数据是否被篡改
    /// </summary>
    [Column("hash_code"), DisplayName("数据哈希值"), Required, MaxLength(64), Excel]
    public virtual string HashCode { get; set; } = null!;
    /// <summary>
    /// 数据密文, 数据被用户数据密钥加密后的结果
    /// </summary>
    [Column("content"), DisplayName("数据密文"), Required, MaxLength(4096), Excel]
    public virtual string Ciphertext { get; set; } = null!;

    /// <summary>
    /// 计算并设置数据的安全数据: 计算 Hash 值和数据密文, 并返回安全数据
    /// </summary>
    public virtual string Security()
    {
        if (this is IDataSerialize dataSerialize)
        {
            var value = dataSerialize.Serialize();
            HashCode = value.GetHashString();
            return value.GetHashString();
        }
        else if (this is IStringSerialize stringSerialize)
        {
            var value = stringSerialize.Serialize();
            HashCode = value.GetHashString();
            return value;
        }
        else
        {
            var ignores = new string[] { nameof(RowVersion).ToCamelCase(), nameof(HashCode).ToCamelCase(), nameof(Ciphertext).ToCamelCase() };
            var fields = this.ToKeyValuePairs().Where(x => !ignores.Contains(x.Key));
            var value = fields.OrderBy(x => x.Key).ToString(AppSettings.DEFAULT_URL_OUTER_SPLITER, AppSettings.DEFAULT_URL_INNER_SPLITER);
            HashCode = value.GetHashString();
            return value;
        }
    }
}